/*
This file is part of La Linea.

La Linea is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

La Linea is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with La Linea.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Frame.h"

#include "CacheManager.h" // Can't be moved to Frame.h

using namespace std;

Frame::Frame(void) : _cache(NULL), _refCounter(0)
{
	_boundingBox.topLeft = Vector2f(0,0);
	_boundingBox.bottomRight = Vector2f(0,0);
	_validBoundingBox = false;
}

Frame::~Frame(void)
{
	CacheManager::getInstance()->getFrameCache()->removeEntry(this);
	vector<OneLineCurve*>::iterator it = _lines.begin();
	for(;it!=_lines.end();++it) delete (*it);
	_lines.clear();
	
	vector<FrameStencil*>::iterator itstencil = _stencilPos.begin();
	for(;itstencil!=_stencilPos.end();++itstencil) delete (*itstencil);
	_stencilPos.clear();
	
	itstencil = _stencilNeg.begin();
	for(;itstencil!=_stencilNeg.end();++itstencil) delete (*itstencil);
	_stencilNeg.clear();
}

Frame* Frame::copy(){
	Frame* newFrame = new Frame();
	vector<OneLineCurve*>::iterator it = _lines.begin();
	for(;it!=_lines.end();++it){
		newFrame->addLine((*it)->copy());
	}
	
	return newFrame;
}

std::vector<OneLineCurve*>& Frame::getLines(){
	return _lines;
}

std::vector<FrameStencil*>& Frame::getStencilPos(){
	return _stencilPos;
}

std::vector<FrameStencil*>& Frame::getStencilNeg(){
	return _stencilNeg;
}

void Frame::addLine(OneLineCurve* c){
	assert (c!=NULL);
	_lines.push_back(c);
}

void Frame::removeLine(OneLineCurve* c){
	assert (c!=NULL);
	std::vector<OneLineCurve*>::iterator it, end;

	for(it = _lines.begin(), end = _lines.end(); it != end; ++it){
		if(*it == c) { delete (*it); _lines.erase(it);  break;}
	}
}

void Frame::deleteNode(Node* selectedNode){
	for(unsigned int i=0; i<this->getLines().size(); i++){
		std::deque<Curve*> curves = this->getLines()[i]->getCurves();
		std::deque<Curve*>::iterator it;

		// Si on a qu'une seule courbe dans le trait
		// et qu'elle contient le noeud selectedNode
		// on supprime tout le trait.
		if(curves.size() == 1){
			if(curves[0]->_origins[0] == selectedNode ||
				curves[0]->_origins[1] == selectedNode){
					this->removeLine(this->getLines()[i]);
					break;
			}
		}

		else if (curves.size() >= 2){
			for(it = curves.begin(); it != curves.end(); ++it){
				Curve* currentCurve = *(it);
				// Si on essaye de supprimer le dernier point
				if((currentCurve->_origins[1] == selectedNode) && (it == curves.end()-1)){
					Curve* precCurve = *(it-1);
					Node* newPrec = new Node();
					newPrec->_pos = precCurve->_origins[1]->_pos;
					newPrec->_size = precCurve->_origins[1]->_size;

					precCurve->_origins[1] = newPrec;

					this->getLines()[i]->removeCurve(currentCurve);
				}
				else if(currentCurve->_origins[0] == selectedNode){

					if(it == curves.begin()){
						this->getLines()[i]->removeCurve(currentCurve);
					}
					else{
						Node* newPrec = new Node();
						Node* newCurrent = new Node();
						Curve* precCurve = *(it-1);
						newPrec->_pos = precCurve->_origins[0]->_pos;
						newPrec->_size = precCurve->_origins[0]->_size;

						newCurrent->_pos = selectedNode->_pos;
						newCurrent->_size = selectedNode->_size;

						Curve* newCurve = new Curve();
						newCurve->_origins[0] = newPrec;
						newCurve->_controlpoint = newCurrent;
						newCurve->_origins[1] = currentCurve->_origins[1];

						if (it-1 == curves.begin()){
							this->getLines()[i]->removeCurve(precCurve);
							this->getLines()[i]->removeCurve(currentCurve);
							this->getLines()[i]->addCurve(newCurve);
						}
						else{
							Curve* precprecCurve = *(it-2);
							precprecCurve->_origins[1] = newPrec;
							this->getLines()[i]->removeCurve(precCurve);
							this->getLines()[i]->replaceCurve(currentCurve,newCurve);
						}
					}
				}
			}
		}
	}
}

void Frame::draw(GLfloat colorr, GLfloat colorg, GLfloat colorb, GLfloat colora, unsigned int options){
	std::vector<OneLineCurve*>::iterator it, end;
	bool initBB = false;
	
	if((options & DRAW_NOSTENCIL) != DRAW_NOSTENCIL) drawStencils(options);
	
	// Draw the lines & compute bounding box coordinates
	for(it = _lines.begin(), end = _lines.end(); it != end; ++it){
		assert ((*it)!=NULL);
		(*it)->draw(colorr, colorg, colorb, colora, options);
		RectangleDesc currentBB = (*it)->getBoundingBox();
		
		if(!initBB){
			_boundingBox.topLeft.x = currentBB.topLeft.x;
			_boundingBox.topLeft.y = currentBB.topLeft.y;
			_boundingBox.bottomRight.x = currentBB.bottomRight.x;
			_boundingBox.bottomRight.y = currentBB.bottomRight.y;
			initBB = true;
		}

		if(currentBB.topLeft.x < _boundingBox.topLeft.x) _boundingBox.topLeft.x = currentBB.topLeft.x;
		if(currentBB.topLeft.y > _boundingBox.topLeft.y) _boundingBox.topLeft.y = currentBB.topLeft.y;

		if(currentBB.bottomRight.x > _boundingBox.bottomRight.x) _boundingBox.bottomRight.x = currentBB.bottomRight.x;
		if(currentBB.bottomRight.y < _boundingBox.bottomRight.y) _boundingBox.bottomRight.y = currentBB.bottomRight.y;
	}
	
	_validBoundingBox = true;
	
	if((options & DRAW_NOCOLOR) != DRAW_NOCOLOR) glColor4f(0.0f, 0.6f, 0.2f, 1.0f);
	if((options & DRAW_BOUNDINGBOX) == DRAW_BOUNDINGBOX){
		drawBoundingBox();
	}
}

void Frame::drawStencils(unsigned int options){
	bool debug = ((options & DRAW_DEBUGSTENCIL) == DRAW_DEBUGSTENCIL);

	// Save the current color
	GLfloat currentColor[4];
	glGetFloatv(GL_CURRENT_COLOR, currentColor);
	
	// 1) Clear the stencil buffer
	glClearStencil(0);
	
	// 2) Draw each "negative" stencil into the stencil buffer
	vector<FrameStencil*>::iterator itstencil = _stencilNeg.begin();

	glEnable(GL_STENCIL_TEST);	
	glStencilFunc(GL_ALWAYS,1,1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	glDepthMask(false);
	
	if(!debug){	
		glColorMask(false, false, false, false);
	}
	else{
		glColor4f(0.8f,0.0f,0.0f,0.8f);
	}
	
	for(;itstencil!=_stencilNeg.end();++itstencil){
		assert ((*itstencil)!=NULL);
		GLuint dList = (*itstencil)->getDisplayList();
		if(dList) glCallList(dList);
	}
	
	// 3) Draw each "positive" stencil into the color buffer
	//    without drawing where negative stencils has been set.
	//    We also save the current color and replace it temporarily
	//    with the clearColor.
	itstencil = _stencilPos.begin();
	
	glDepthMask(true);
	glStencilFunc(GL_NOTEQUAL, 1, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	
	if(!debug){
		glColorMask(true, true, true, true);
	
		GLfloat backgroundColor[4];
		glGetFloatv(GL_COLOR_CLEAR_VALUE, backgroundColor);
		glColor4fv(backgroundColor);
	}	
	else{
		glColor4f(0.0f,0.8f,0.0f,0.8f);
	}
	
	for(;itstencil!=_stencilPos.end();++itstencil){
		assert ((*itstencil)!=NULL);
		GLuint dList = (*itstencil)->getDisplayList();
		if(dList) glCallList(dList);
	}
	
	glColor4fv(currentColor);
	
	glDisable(GL_STENCIL_TEST);
}

void Frame::drawBoundingBox(){
	if(!_validBoundingBox) getBoundingBox();
	glBegin(GL_LINE_STRIP);
		glVertex2f(_boundingBox.topLeft.x, _boundingBox.topLeft.y);
		glVertex2f(_boundingBox.topLeft.x, _boundingBox.bottomRight.y);
		glVertex2f(_boundingBox.bottomRight.x, _boundingBox.bottomRight.y);
		glVertex2f(_boundingBox.bottomRight.x, _boundingBox.topLeft.y);			
		glVertex2f(_boundingBox.topLeft.x, _boundingBox.topLeft.y);
	glEnd();
}

GLuint Frame::createDisplayList(){
	GLuint res = glGenLists(1);
	glNewList(res, GL_COMPILE);
	this->draw(1.0,1.0,1.0,1.0, DRAW_BORDERLINE | DRAW_NOCOLOR | DRAW_NOSTENCIL);
	glEndList();
	return res;
}

RectangleDesc Frame::getBoundingBox(){
	if(!_validBoundingBox)
	{
		bool init = false;
		_boundingBox.topLeft.x = 0.0;
		_boundingBox.topLeft.y = 0.0;
		_boundingBox.bottomRight.x = 0.0;
		_boundingBox.bottomRight.y = 0.0;

		std::vector<OneLineCurve*>::iterator it, end;

		for(it = _lines.begin(), end = _lines.end(); it != end; ++it){
				assert ((*it)!=NULL);
				RectangleDesc current = (*it)->getBoundingBox();

				if(!init){
					_boundingBox.topLeft.x = current.topLeft.x;
					_boundingBox.topLeft.y = current.topLeft.y;
					_boundingBox.bottomRight.x = current.bottomRight.x;
					_boundingBox.bottomRight.y = current.bottomRight.y;
					init = true;
				}

			if(current.topLeft.x < _boundingBox.topLeft.x) _boundingBox.topLeft.x = current.topLeft.x;
			if(current.topLeft.y > _boundingBox.topLeft.y) _boundingBox.topLeft.y = current.topLeft.y;
					
			if(current.bottomRight.x > _boundingBox.bottomRight.x) _boundingBox.bottomRight.x = current.bottomRight.x;
			if(current.bottomRight.y < _boundingBox.bottomRight.y) _boundingBox.bottomRight.y = current.bottomRight.y;
		}
		
		_validBoundingBox = true;
	}
	
	return _boundingBox;
}

FrameCacheData* Frame::getCache(){
	return _cache;
}

void Frame::setCache(FrameCacheData* c){
	_cache = c;
}

void Frame::tesselateStencils(){
	vector<FrameStencil*>::iterator it = _stencilPos.begin();
	for(;it!=_stencilPos.end();++it){
		assert ((*it)!=NULL);
		(*it)->tesselateContour();
	}
	
	it = _stencilNeg.begin();
	for(;it!=_stencilNeg.end();++it){
		assert ((*it)!=NULL);
		(*it)->tesselateContour();
	}
}

void Frame::addPosStencil(FrameStencil* s){
	assert (s!=NULL);
	_stencilPos.push_back(s);
}
	
void Frame::addNegStencil(FrameStencil* s){
	assert (s!=NULL);
	_stencilNeg.push_back(s);
}

void Frame::deleteStencils(){
	vector<FrameStencil*>::iterator it = _stencilPos.begin();
	for(;it!=_stencilPos.end();++it){
		assert ((*it)!=NULL);
		delete (*it);
	}
	_stencilPos.clear();
	
	it = _stencilNeg.begin();
	for(;it!=_stencilNeg.end();++it){
		assert ((*it)!=NULL);
		delete (*it);
	}
	_stencilNeg.clear();
}

Node* Frame::getControlNode(Node* n1, Node* n2){
	Node* res = 0;
	vector<OneLineCurve*>::iterator lines = this->getLines().begin();
	for(; lines<this->getLines().end(); ++lines){
		deque<Curve*>::iterator curves = (*lines)->getCurves().begin();
		for(; curves != (*lines)->getCurves().end(); ++curves){
			Curve* current = *curves;
			if((current->_origins[0] == n1 && current->_origins[1] == n2) || 
			   (current->_origins[1] == n1 && current->_origins[0] == n2)){
				res = current->_controlpoint;
				return res;
			}
		}
	}
	return res;
}

int Frame::incRefCounter(){
	return ++_refCounter;
}

int Frame::decRefCounter(){
	return --_refCounter;
}

int Frame::getRefCounter(){
	return _refCounter;
}